/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.web.command.component.serializer;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.inspur.edp.caf.cef.schema.base.utils.JsonUtil;
import com.inspur.edp.cdp.cdf.component.manager.serializer.CompOperationSerializer;
import com.inspur.edp.cdp.cdf.component.manager.serializer.ComponentSerializer;
import com.inspur.edp.component.schema.AbstractComponent;
import com.inspur.edp.web.command.component.ComponentConstant;
import com.inspur.edp.web.command.component.metadata.BranchCollectionCommandItem;
import com.inspur.edp.web.command.component.metadata.BranchCommandItem;
import com.inspur.edp.web.command.component.metadata.CmpMethodParamConfig;
import com.inspur.edp.web.command.component.metadata.CmpMethodRefering;
import com.inspur.edp.web.command.component.metadata.CommandItem;
import com.inspur.edp.web.command.component.metadata.CommandItemType;
import com.inspur.edp.web.command.component.metadata.ConditionType;
import com.inspur.edp.web.command.component.metadata.ExtendProperty;
import com.inspur.edp.web.command.component.metadata.Parameter;
import com.inspur.edp.web.command.component.metadata.WebCommand;
import com.inspur.edp.web.command.component.metadata.WebCommandsMetadata;
import com.inspur.edp.web.command.component.serializer.CommandItem.EditorInfoSerializer;

import java.util.List;
import java.util.Map;

/**
 * @author Kaixuan Shi
 * @version 0.1
 */
public class WebCmdComponentSerializer extends ComponentSerializer {
    private static final String CLASS_VERSION = "v2.0";

    @Override
    protected void readExtendInfo(AbstractComponent abstractComponent, JsonNode compNode) {
        WebCommandsMetadata cmdComponent = (WebCommandsMetadata) abstractComponent;
        if (JsonUtil.isExistsProp(compNode, ComponentConstant.CLASS_VERSION)) {
            this.readExtendProperty(cmdComponent, compNode);
        } else {//旧版数据结构需特殊处理，需反序列化整全部内容
            this.readOldStructure(cmdComponent, compNode);
        }
    }

    private void readExtendProperty(WebCommandsMetadata cmdComponent, JsonNode compNode) {
        JsonNode propertyNode = JsonUtil.getPropValue(compNode, ComponentConstant.EXTEND_PROPERTY, JsonNode.class);
        if (propertyNode == null || propertyNode.isEmpty()) {
            return;
        }
        ExtendProperty extendProperty = new ExtendProperty();
        if (JsonUtil.isExistsProp(propertyNode, ComponentConstant.ExtendPropertyField.IS_COMMON)) {
            extendProperty.setCommon(JsonUtil.getPropValue(propertyNode, ComponentConstant.ExtendPropertyField.IS_COMMON, boolean.class));
        }
        extendProperty.setFormCode(JsonUtil.getPropValue(propertyNode, ComponentConstant.ExtendPropertyField.FORM_CODE, String.class));
        cmdComponent.setExtendProperty(extendProperty);
    }

    @Override
    protected void writeExtendInfo(AbstractComponent abstractComponent, JsonNode compNode) {
        WebCommandsMetadata cmdComponent = (WebCommandsMetadata) abstractComponent;
        JsonUtil.setPropValue(compNode, ComponentConstant.CLASS_VERSION, CLASS_VERSION);
        this.writeExtendProperty(cmdComponent, compNode);
    }

    private void writeExtendProperty(WebCommandsMetadata cmdComponent, JsonNode compNode) {
        ExtendProperty extendProperty = cmdComponent.getExtendProperty();
        if (extendProperty == null) {
            return;
        }
        JsonNode propertyNode = (new ObjectMapper()).createObjectNode();
        JsonUtil.setPropValue(propertyNode, ComponentConstant.ExtendPropertyField.FORM_CODE, extendProperty.getFormCode());
        JsonUtil.setPropValue(propertyNode, ComponentConstant.ExtendPropertyField.IS_COMMON, extendProperty.isCommon());
        JsonUtil.setPropValue(compNode, ComponentConstant.EXTEND_PROPERTY, propertyNode);
    }

    @Override
    protected CompOperationSerializer getOperationSerializer() {
        return new WebCommandSerializer();
    }

    @Override
    protected AbstractComponent createComponent() {
        return new WebCommandsMetadata();
    }

    //region 旧版数据反序列化
    private void readOldStructure(WebCommandsMetadata cmdComponent, JsonNode compNode) {
        cmdComponent.setId(JsonUtil.getPropValue(compNode, ComponentConstant.OldComponentField.ID, String.class));
        cmdComponent.setCode(JsonUtil.getPropValue(compNode, ComponentConstant.OldComponentField.CODE, String.class));
        cmdComponent.setName(JsonUtil.getPropValue(compNode, ComponentConstant.OldComponentField.NAME, String.class));
        cmdComponent.setDescription(JsonUtil.getPropValue(compNode, ComponentConstant.OldComponentField.DESCRIPTION, String.class));
        this.readOldExtendProperty(cmdComponent, compNode);
        this.readOldCommands(cmdComponent, compNode);
    }

    private void readOldExtendProperty(WebCommandsMetadata cmdComponent, JsonNode compNode) {
        JsonNode extendNode = JsonUtil.getPropValue(compNode, ComponentConstant.OldComponentField.EXTENDS, JsonNode.class);
        if (extendNode == null || extendNode.isEmpty()) {
            return;
        }
        ExtendProperty extendProperty = new ExtendProperty();
        extendProperty.setFormCode(JsonUtil.getPropValue(extendNode, ComponentConstant.OldExtendPropertyField.FORM_CODE, String.class));
        if (JsonUtil.isExistsProp(extendNode, ComponentConstant.OldExtendPropertyField.IS_COMMON)) {
            extendProperty.setCommon(JsonUtil.getPropValue(extendNode, ComponentConstant.OldExtendPropertyField.IS_COMMON, boolean.class));
        }
        cmdComponent.setExtendProperty(extendProperty);
    }

    private void readOldCommands(WebCommandsMetadata cmdComponent, JsonNode compNode) {
        ArrayNode commandNodes = JsonUtil.getPropValue(compNode, ComponentConstant.OldComponentField.COMMANDS, ArrayNode.class);
        if (commandNodes == null || commandNodes.isEmpty()) {
            return;
        }
        List<WebCommand> commands = cmdComponent.getOperations();
        for (JsonNode commandNode : commandNodes) {
            WebCommand command = new WebCommand();
            command.setId(JsonUtil.getPropValue(commandNode, ComponentConstant.OldCommandField.ID, String.class));
            command.setCode(JsonUtil.getPropValue(commandNode, ComponentConstant.OldCommandField.CODE, String.class));
            command.setName(JsonUtil.getPropValue(commandNode, ComponentConstant.OldCommandField.NAME, String.class));
            command.setDescription(JsonUtil.getPropValue(commandNode, ComponentConstant.OldCommandField.DESCRIPTION, String.class));
            this.readOldParameters(command, commandNode);
            this.readOldCommandItems(command, commandNode);
            commands.add(command);
        }
    }

    private void readOldParameters(WebCommand command, JsonNode commandNode) {
        ArrayNode parameterNodes = JsonUtil.getPropValue(commandNode, ComponentConstant.OldCommandField.PARAMETERS, ArrayNode.class);
        if (parameterNodes == null || parameterNodes.isEmpty()) {
            return;
        }
        List<Parameter> parameters = command.getCompParameters();
        for (JsonNode paramNode : parameterNodes) {
            Parameter param = new Parameter();
            param.setId(JsonUtil.getPropValue(paramNode, ComponentConstant.OldParameterField.ID, String.class));
            param.setCode(JsonUtil.getPropValue(paramNode, ComponentConstant.OldParameterField.CODE, String.class));
            param.setName(JsonUtil.getPropValue(paramNode, ComponentConstant.OldParameterField.NAME, String.class));
            param.setDescription(JsonUtil.getPropValue(paramNode, ComponentConstant.OldParameterField.DESCRIPTION, String.class));
            param.setParamType(JsonUtil.getPropValue(paramNode, ComponentConstant.OldParameterField.PARAM_TYPE, String.class));
            param.setEditorType(JsonUtil.getPropValue(paramNode, ComponentConstant.OldParameterField.EDITOR_TYPE, String.class));
            param.setDefaultValue(JsonUtil.getPropValue(paramNode, ComponentConstant.OldParameterField.DEFAULT_VALUE, String.class));
            if (JsonUtil.isExistsProp(paramNode, ComponentConstant.OldParameterField.CONTROL_SOURCE)) {
                JsonNode controlSourceNode = JsonUtil.getPropValue(paramNode, ComponentConstant.OldParameterField.CONTROL_SOURCE, JsonNode.class);
                EditorInfoSerializer editorInfoSerializer = new EditorInfoSerializer();
                param.setCustomControlSource(editorInfoSerializer.deSerialize(controlSourceNode));
            }
            if (JsonUtil.isExistsProp(paramNode, ComponentConstant.OldParameterField.IS_RET_VAL)) {
                param.setRetVal(JsonUtil.getPropValue(paramNode, ComponentConstant.OldParameterField.IS_RET_VAL, boolean.class));
            }
            parameters.add(param);
        }
    }

    private void readOldCommandItems(WebCommand command, JsonNode commandNode) {
        ArrayNode itemNodes = JsonUtil.getPropValue(commandNode, ComponentConstant.OldCommandField.ITEMS, ArrayNode.class);
        if (itemNodes == null || itemNodes.isEmpty()) {
            return;
        }
        List<CommandItem> commandItems = command.getItems();
        for (JsonNode itemNode : itemNodes) {
            CommandItem item = this.readOldCommandItem(itemNode);
            commandItems.add(item);
        }
    }

    private CommandItem readOldCommandItem(JsonNode itemNode) {
        //CommandItemType itemType = JsonUtil.getPropValue(itemNode, ComponentConstant.OldCommandItemField.ITEM_TYPE, CommandItemType.class);
        //todo 旧版元数据结构，序列化了两次，前端设计器传值时，只传入了{"Type":0,"Content":{}}，暂时兼容
        CommandItemType itemType = JsonUtil.getPropValue(itemNode, "Type", CommandItemType.class);
        JsonNode itemNodeContent = JsonUtil.getPropValue(itemNode, "Content", JsonNode.class);
        CommandItem item;
        switch (itemType) {
            case BranchCollection:
                item = this.readOldBranchCollectionCommandItem(itemNodeContent);
                break;
            case Branch:
                item = this.readOldBranchCommandItem(itemNodeContent);
                break;
            case MethodRefer:
                item = this.readOldCmpMethodRefering(itemNodeContent);
                break;
            default:
                throw new RuntimeException("不支持的CommandItem类型：" + itemType);
        }
        return item;
    }

    private CommandItem readOldBranchCollectionCommandItem(JsonNode itemNode) {
        BranchCollectionCommandItem collectionItem = new BranchCollectionCommandItem();
        collectionItem.setId(JsonUtil.getPropValue(itemNode, ComponentConstant.OldBranchCollectionCommandItemField.ID, String.class));
        collectionItem.setCode(JsonUtil.getPropValue(itemNode, ComponentConstant.OldBranchCollectionCommandItemField.CODE, String.class));
        collectionItem.setName(JsonUtil.getPropValue(itemNode, ComponentConstant.OldBranchCollectionCommandItemField.NAME, String.class));

        ArrayNode itemsArrayNode = JsonUtil.getPropValue(itemNode, ComponentConstant.OldBranchCollectionCommandItemField.ITEMS, ArrayNode.class);
        if (itemsArrayNode == null || itemsArrayNode.isEmpty()) {
            return collectionItem;
        }
        List<BranchCommandItem> items = collectionItem.getItems();
        for (JsonNode commandItemNode : itemsArrayNode) {
            BranchCommandItem commandItem = (BranchCommandItem) this.readOldBranchCommandItem(commandItemNode);
            items.add(commandItem);
        }
        return collectionItem;
    }

    private CommandItem readOldBranchCommandItem(JsonNode itemNode) {
        BranchCommandItem branchCommandItem = new BranchCommandItem();
        branchCommandItem.setId(JsonUtil.getPropValue(itemNode, ComponentConstant.OldBranchCommandItemField.ID, String.class));
        branchCommandItem.setCode(JsonUtil.getPropValue(itemNode, ComponentConstant.OldBranchCommandItemField.CODE, String.class));
        branchCommandItem.setName(JsonUtil.getPropValue(itemNode, ComponentConstant.OldBranchCommandItemField.NAME, String.class));
        branchCommandItem.setConditionType(JsonUtil.getPropValue(itemNode, ComponentConstant.OldBranchCommandItemField.CONDITION_TYPE, ConditionType.class));
        branchCommandItem.setExpress(JsonUtil.getPropValue(itemNode, ComponentConstant.OldBranchCommandItemField.EXPRESS, String.class));
        ArrayNode itemsArrayNode = JsonUtil.getPropValue(itemNode, ComponentConstant.OldBranchCommandItemField.ITEMS, ArrayNode.class);
        if (itemsArrayNode == null || itemsArrayNode.isEmpty()) {
            return branchCommandItem;
        }
        List<CommandItem> commandItems = branchCommandItem.getItems();
        for (JsonNode commandItemNode : itemsArrayNode) {
            CommandItem item = this.readOldCommandItem(commandItemNode);
            commandItems.add(item);
        }
        return branchCommandItem;
    }

    private CommandItem readOldCmpMethodRefering(JsonNode itemNode) {
        CmpMethodRefering cmpMethodRefering = new CmpMethodRefering();
        cmpMethodRefering.setId(JsonUtil.getPropValue(itemNode, ComponentConstant.OldCmpMethodReferingField.ID, String.class));
        cmpMethodRefering.setCode(JsonUtil.getPropValue(itemNode, ComponentConstant.OldCmpMethodReferingField.CODE, String.class));
        cmpMethodRefering.setName(JsonUtil.getPropValue(itemNode, ComponentConstant.OldCmpMethodReferingField.NAME, String.class));
        cmpMethodRefering.setComponentId(JsonUtil.getPropValue(itemNode, ComponentConstant.OldCmpMethodReferingField.COMPONENT_ID, String.class));
        cmpMethodRefering.setComponentCode(JsonUtil.getPropValue(itemNode, ComponentConstant.OldCmpMethodReferingField.COMPONENT_CODE, String.class));
        cmpMethodRefering.setComponentName(JsonUtil.getPropValue(itemNode, ComponentConstant.OldCmpMethodReferingField.COMPONENT_NAME, String.class));
        cmpMethodRefering.setComponentPath(JsonUtil.getPropValue(itemNode, ComponentConstant.OldCmpMethodReferingField.COMPONENT_PATH, String.class));
        cmpMethodRefering.setMethodId(JsonUtil.getPropValue(itemNode, ComponentConstant.OldCmpMethodReferingField.METHOD_ID, String.class));
        cmpMethodRefering.setMethodCode(JsonUtil.getPropValue(itemNode, ComponentConstant.OldCmpMethodReferingField.METHOD_CODE, String.class));
        cmpMethodRefering.setMethodName(JsonUtil.getPropValue(itemNode, ComponentConstant.OldCmpMethodReferingField.METHOD_NAME, String.class));
        if (JsonUtil.isExistsProp(itemNode, ComponentConstant.CmpMethodReferingField.REPLACED)) {
            cmpMethodRefering.setReplaced(JsonUtil.getPropValue(itemNode, ComponentConstant.OldCmpMethodReferingField.REPLACED, boolean.class));
        }
        if (JsonUtil.isExistsProp(itemNode, ComponentConstant.CmpMethodReferingField.BEFORE_EXPANSION)) {
            cmpMethodRefering.setBeforeExpansion(JsonUtil.getPropValue(itemNode, ComponentConstant.OldCmpMethodReferingField.BEFORE_EXPANSION, boolean.class));
        }
        if (JsonUtil.isExistsProp(itemNode, ComponentConstant.CmpMethodReferingField.AFTER_EXPANSION)) {
            cmpMethodRefering.setAfterExpansion(JsonUtil.getPropValue(itemNode, ComponentConstant.OldCmpMethodReferingField.AFTER_EXPANSION, boolean.class));
        }
        ArrayNode paramConfigsNode = JsonUtil.getPropValue(itemNode, ComponentConstant.OldCmpMethodReferingField.PARAM_CONFIGS, ArrayNode.class);
        if (paramConfigsNode == null || paramConfigsNode.isEmpty()) {
            return cmpMethodRefering;
        }
        List<CmpMethodParamConfig> paramConfigs = cmpMethodRefering.getParamConfigs();
        for (JsonNode paramConfigNode : paramConfigsNode) {
            CmpMethodParamConfig paramConfig = new CmpMethodParamConfig();
            paramConfig.setParamCode(JsonUtil.getPropValue(paramConfigNode, ComponentConstant.OldCmpMethodParamConfigField.PARAM_CODE, String.class));
            paramConfig.setParamName(JsonUtil.getPropValue(paramConfigNode, ComponentConstant.OldCmpMethodParamConfigField.PARAM_NAME, String.class));
            paramConfig.setParamExpress(JsonUtil.getPropValue(paramConfigNode, ComponentConstant.OldCmpMethodParamConfigField.PARAM_EXPRESS, String.class));
            paramConfigs.add(paramConfig);
        }
        return cmpMethodRefering;
    }
    //endregion
}
